package sdmaker.parser;

import java.util.ArrayList;
import java.util.List;

import fsa.State;
import fsa.Transition;


public class StateData {
	
	private static List<StateData> singletonStates= new ArrayList<StateData>();
	private State state;
	private List<Transition> alreadyParsed;
	private List<Transition> neverParsed;
	private List<Transition> successeurs;
	private List<Transition> predecesseurs;

	
	
	/* ************************ *
	 * ***** CONSTRUCTORS ***** *
	 * ************************ */
	
	private StateData(){} //never use

	private StateData(State state){
		this.state= state;
		this.alreadyParsed= new ArrayList<Transition>();
		this.neverParsed= new ArrayList<Transition>();

		for(Transition t : state.getSuccesseurs()){
			neverParsed.add(t);
		}

		this.successeurs= state.getSuccesseurs();
		this.predecesseurs= state.getPredecesseurs();
	}

	private StateData(
			State state, 
			List<Transition>alreadyParsed, 
			List<Transition>neverParsed,
			List<Transition> successeurs,
			List<Transition> predecesseurs){
		this.state= state;
		this.alreadyParsed= alreadyParsed;
		this.neverParsed= neverParsed;
		this.successeurs= successeurs;
		this.predecesseurs= predecesseurs;
	}

	/* Equivalent à un constructeur. En faite, c'est meme le seule
	 * moyen d'obtenir le StateData d'un State (design patter Singleton) */
	public static StateData getStateData(State state){
		StateData finalStateData=null;

		for(StateData stateData : singletonStates){
			if(Utils.sameState(state, stateData.getState())){
				finalStateData= stateData;
			}
		}

		if(finalStateData==null){
			finalStateData= new StateData(state);
			singletonStates.add(finalStateData);
		}

		return finalStateData;
	}

	
	
	/* ************************ *
	 * ******** GETTERS ******* *
	 * ************************ */
	
	public StateData getSimpleStateData(int i){
		Transition successeur= successeurs.get(i);
		List<Transition> successeurs= new ArrayList<Transition>();

		successeurs.add(successeur);

		return new StateData(
				state,
				alreadyParsed,
				neverParsed,
				successeurs,
				predecesseurs);
	}
	
	public StateData getSimpleStateData(StateData stateData){
		StateData simpleStateData=null;
		for(int i= 0; i < successeurs.size(); i++){
			StateData target= StateData.getStateData(successeurs.get(i).getTarget());
			if(target.equals(stateData)){
				simpleStateData= getSimpleStateData(i);
				break;
			}
		}
		
		return simpleStateData;
	}

	public State getState(){
		return state;
	}

	public List<Transition> getSuccesseurs(){
		return successeurs;
	}

	public Transition getSuccesseur(int index){
		return successeurs.get(index);
	}

	public Transition getPredecesseur(int index){
		return predecesseurs.get(index);
	}

	public List<Transition> getPredecesseurs(){
		return predecesseurs;
	}

	public List<Transition> getAlreadyParsed(){
		return alreadyParsed;
	}

	public int getNbSuccesseur(){
		return successeurs.size();
	}

	public String getName(){
		return state.getName();
	}

	public StateData getNextStateData(int i){
		StateData stateData= null;

		if(getNbSuccesseur()>i){
			stateData= getStateData(successeurs.get(i).getTarget());
		}

		return stateData;
	}
	
	public SetStateData getAllNextStateData(){
		SetStateData allNext= new SetStateData();
		
		for(int i= 0; i < successeurs.size(); i++){
			allNext.add(StateData.getStateData(successeurs.get(i).getTarget()));
		}
		
		return allNext;
	}
	
	public Transition getLeadingTransition(StateData stateData){
		Transition leadingTransition= null;
		
		for(Transition succ: successeurs){
			StateData target= StateData.getStateData(succ.getTarget());
			
			
			if(stateData.equals(target)){
				leadingTransition= succ;
				System.out.println("wanted: "+stateData);
				System.out.println("getted: "+target);
				System.out.println("by: "+succ);
				break;
			}
		}
		
		return leadingTransition;
	}

	
	
	/* ********************************* *
	 * **** SETTERS/ADDERS/REMOVERS **** *
	 * ********************************* */
	
	public void addParsedTransition(Transition t){		
		for(Transition tmp : successeurs){
			if(Utils.sameTransition(t, tmp)){
				if(isAlreadyParsedTransition(t)==false){
					alreadyParsed.add(t);

					int i= 0;
					for(i= 0; i < neverParsed.size(); i++){
						if(Utils.sameTransition(t, neverParsed.get(i))==true){
							break;
						}
					}

					if(0<= i && i < neverParsed.size()){
						neverParsed.remove(i);
					}
				}
			}
		}
	}
	
	public void addParsedTransition(StateData stateData){
		for(Transition succ: successeurs){
			StateData target= StateData.getStateData(succ.getTarget());
			
			if(stateData.equals(target)){
				if(isAlreadyParsedTransition(succ)==false){
					alreadyParsed.add(succ);
				}
				
				int i= 0;
				for(i= 0; i < neverParsed.size(); i++){
					if(Utils.sameTransition(succ, neverParsed.get(i))==true){
						break;
					}
				}

				if(0<= i && i < neverParsed.size()){
					neverParsed.remove(i);
				}
			}
		}
	}
	


	/* ************************ *
	 * ******* TESTERS ******** *
	 * ************************ */
	
	public boolean equals(StateData stateData){
		return Utils.sameState(state, stateData.getState());
	}

	public boolean isAlreadyParsedTransition(Transition t){
		boolean isAlreadyParsedTransition= false;

		for(Transition tmp : alreadyParsed){
			if(Utils.sameTransition(t, tmp)){
				isAlreadyParsedTransition= true;
				break;
			}
		}

		return isAlreadyParsedTransition;
	}

	public boolean fullParsed(SetStateData forbiddenState){
		boolean fullParsed=true;
		
		/* Algorithme IDEA: 
		 * A State is parsed if all its allowed
		 * successors Transition are parsed.
		 */
		
		for(Transition t : successeurs){
			StateData target= StateData.getStateData(t.getTarget());
			boolean isParsed= false;
			
			for(Transition parsed : alreadyParsed){
				if(Utils.sameTransition(t, parsed)){
					isParsed=true;
					break;
				}
			}

			if(isParsed==false&&forbiddenState.contains(target)==false){
				fullParsed=false;
				break;
			}
		}

		return fullParsed;
	}
	
	
	
	/* ************************ *
	 * ******** OTHERS ******** *
	 * ************************ */
	
	public String toString(){
		return state.toString();
	}
}
